<template>
  <div class="image-compress">
    <input ref="fileInput" type="file" @change="onChange" />
    <div ref="preview" class="preview-wrapper"></div>
  </div>
</template>

<script lang="ts" setup>
type CompressSuccess = (blob: Blob) => void
const props = defineProps<{ onSuccess?: CompressSuccess }>()

// 1M = 1kb * 1024   1kb = 1bit * 1024
const MAXSIZE = 12 * 1024 * 1024
const MAXSIZE_STR = '12MB'
const ACCEPT = ['image/jpeg', 'image/jpg', 'image/png']

const fileInput = ref(null)
const preview = ref(null)

function onChange(e: Event) {
  const target = e.target as HTMLInputElement
  const file = target.files[0]
  // console.log(file)
  if (!file) {
    return
  }
  const { size: fileSize, type: fileType } = file
  if (!ACCEPT.includes(fileType)) {
    console.log(`不支持${fileType}文件类型！`)
    fileInput.value.value = ''
    return
  }
  if (fileSize > MAXSIZE) {
    console.log(`文件超出${MAXSIZE_STR}！`)
    fileInput.value.value = ''
    return
  }
  convertImageToBase64(file)
}

function convertImageToBase64(file: File) {
  let reader = new FileReader()
  reader.addEventListener('load', e => {
    const base64Image = e.target.result
    compress(base64Image as string)
    reader = null
  })
  // blob 是浏览器提供的一种类型 主要是标记文件类型
  reader.readAsDataURL(file)
}

// 压缩文件的两个方向： 1、图片的尺寸   2、图片输出的质量
function compress(base64Image: string) {
  // console.log(base64Image)
  let maxWidth = 1024
  let maxHeight = 1024
  const image = new Image()
  image.addEventListener('load', () => {
    let ratio: number //图片的压缩比
    let needCompress = false //是否需要压缩
    // 图片实际高宽
    const naturalWidth = image.naturalWidth
    const naturalHeight = image.naturalHeight
    // 校验图片是否需要压缩
    if (naturalWidth > maxWidth) {
      needCompress = true
      ratio = naturalWidth / maxWidth
      maxHeight = naturalHeight / ratio
    }
    if (naturalHeight > maxHeight) {
      needCompress = true
      ratio = naturalHeight / maxHeight
      maxWidth = naturalWidth / ratio
    }
    // 如果不需要压缩，需要获取图片的实际尺寸
    if (!needCompress) {
      maxWidth = naturalWidth
      maxHeight = naturalHeight
    }

    // 1、先构建 canvas
    const canvas = document.createElement('canvas')
    canvas.setAttribute('id', '__compress__')
    canvas.width = maxWidth
    canvas.height = maxHeight
    // canvas 在转换的过程中不需要被看见
    canvas.style.visibility = 'hidden'
    preview.value.appendChild(canvas)

    // 2、canvas 绘图 导出压缩后的 base64图片
    const ctx = canvas.getContext('2d')
    // 先清空一个矩形空间内的所有像素，比如这里的canvas反复重用的时候，就需要清除像素再绘制图片，不然两张图就会出现叠加的效果
    ctx.clearRect(0, 0, maxWidth, maxHeight)
    ctx.drawImage(image, 0, 0, maxWidth, maxHeight)
    const compressImage = canvas.toDataURL('image/jpeg', 0.9)
    // console.log(compressImage)
    if (props.onSuccess) {
      props.onSuccess(dataURLtoBlob(compressImage))
    }
    const _image = new Image()
    _image.src = compressImage
    preview.value.appendChild(_image)
    // 压缩的后的图片已经保存在内存中，再把 canvas 从 DOM 上移除
    canvas.remove()
    console.log(`压缩比：${image.src.length / _image.src.length}`)
  })
  image.src = base64Image
  preview.value.appendChild(image)
}

function dataURLtoBlob(dataURI: string) {
  const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] // mime类型
  const byteString = atob(dataURI.split(',')[1]) //base64 解码
  const arrayBuffer = new ArrayBuffer(byteString.length) //创建缓冲数组
  const intArray = new Uint8Array(arrayBuffer) //创建视图

  for (let i = 0; i < byteString.length; i++) {
    intArray[i] = byteString.charCodeAt(i)
  }
  return new Blob([intArray], { type: mimeString })
}
</script>
<style lang="scss" scoped></style>
